#include "Land.h"

#include <cassert>

#include<D3D10.h>
#include <D3dx10math.h>

#include "D3DErrorChecker.h"
#include "MathUtils.h"

#include "Vertex.h"

namespace D3D10Utils
{
	Land::Land(const unsigned __int32 rows, const unsigned __int32 columns)
		: mDevice(0)
		, mVertexBuffer(0)
		, mIndexBuffer(0)
		, mNumVertices(rows * columns)
		, mNumFaces((rows - 1) * (columns - 1) * 2)
		, mRows(rows)
		, mColumns(columns)
	{
		assert(rows > 0 && "Grid::Grid: Rows must be a positive number");
		assert(columns > 0 && "Grid::Grid: Columns must be a positive number");
	}

	Land::~Land()
	{
		mVertexBuffer->Release();
		mIndexBuffer->Release();
	}

	void Land::init(ID3D10Device * const device, const float cellSpacingX, const float cellSpacingZ)
	{
		if (!device)
		{
			MessageBox(0, L"NULL pointer", 0, 0);
			PostQuitMessage(0);
		}

		mDevice = device;
				
		// Compute the half width and height of the grid.		
		// Our grid will be at xz plane, that the reason why we ask for cell
		// spacing in that coordinates.
		const float halfWidth = (mColumns - 1) * cellSpacingX * 0.5f;
		const float halfHeight = (mRows - 1) * cellSpacingZ * 0.5f;
		
		// Create vertices and indices
		Vertex *vertices = createVertices(cellSpacingX, cellSpacingZ);
		unsigned __int32 *indices = createIndices();

		computeNormals(vertices, indices);

		initVertexBuffer(vertices);
		initIndexBuffer(indices);		
		
		delete[] vertices;
		delete[] indices;
	}

	void Land::draw()
	{
		const unsigned __int32 stride = sizeof(Vertex);
		const unsigned __int32 offset = 0;
		mDevice->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &offset);
		mDevice->IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R32_UINT, 0);

		mDevice->DrawIndexed(mNumFaces * 3, 0, 0);
	}

	Vertex* Land::createVertices(const float cellSpacingX, const float cellSpacingZ)
	{
		// Compute the half width and height of the grid.		
		// Our grid will be at xz plane, that the reason why we ask for cell
		// spacing in that coordinates.
		const float halfWidth = (mColumns - 1) * cellSpacingX * 0.5f;
		const float halfHeight = (mRows - 1) * cellSpacingZ * 0.5f;

		// Compute the delta between texels according to
		// land grid dimensions.
		const float du = 1.0f / (mColumns - 1);
		const float dv = 1.0f / (mRows - 1);

		// Create vertices:
		// Vij = (-0.5 * width + i * cellSpacingX, 0.5 * height - j * cellSpacingZ)
		Vertex *vertices = new Vertex[mNumVertices];
		for(unsigned __int32 j = 0; j < mColumns; ++j)
		{
			const float x = -halfWidth + j * cellSpacingX;

			for(unsigned __int32 i = 0; i < mRows; ++i)
			{
				const float z = halfHeight - i * cellSpacingZ;
				const float y = getHeight(x, z);

				// Update the vertex
				const unsigned __int32 vertexIndex = i * mRows + j;
				vertices[vertexIndex].mPosition = D3DXVECTOR3(x, y, z);

				// Stretch texture over grid.
				vertices[vertexIndex].mTexCoord.x = j * du;
				vertices[vertexIndex].mTexCoord.y = i * dv;

				// n = (-df/dx, 1, -df/dz)
				D3DXVECTOR3 normal;
				normal.x = -0.03f * z * cosf(0.1f * x) - 0.3f * cosf(0.1f * z);
				normal.y = 1.0f;
				normal.z = -0.3f * sinf(0.1f * x) + 0.03f * x * sinf(0.1f * z);
				D3DXVec3Normalize(&vertices[vertexIndex].mNormal, &normal);
			}
		}

		return vertices;
	}

	unsigned __int32* Land::createIndices()
	{
		unsigned __int32 *indices = new unsigned __int32[mNumFaces * 3];
		unsigned __int32 quadOffset = 0;
		for(unsigned __int32 j = 0; j < mColumns - 1; ++j)
		{
			for(unsigned __int32 i = 0; i < mRows - 1; ++i)
			{
				// Define the needed vertices.
				const unsigned __int32 upperLeft = i * mRows + j;
				const unsigned __int32 upperRight = i * mRows + j + 1;
				const unsigned __int32 bottomLeft = (i + 1) * mRows + j;
				const unsigned __int32 bottomRight = (i + 1) * mRows + j + 1;

				indices[quadOffset] = upperLeft;
				indices[quadOffset + 1] = upperRight;
				indices[quadOffset + 2] = bottomLeft;

				indices[quadOffset + 3] = bottomLeft;
				indices[quadOffset + 4] = upperRight;
				indices[quadOffset + 5] = bottomRight;

				// Next quad
				quadOffset += 6; 
			}
		}

		return indices;
	}

	void Land::computeNormals(Vertex * const vertices, const unsigned __int32 * const indices)
	{
		// For each triangle in the mesh.
		const unsigned __int32 numTriangles = (mNumFaces % 2 == 0) ? mNumFaces / 2 : (mNumFaces - 1) / 2 + 1; 
		for (unsigned __int32 i = 0; i < numTriangles; ++i)
		{
			// indices of the ith triangle.
			unsigned __int32 i0 = indices[i * 3 + 0];
			unsigned __int32 i1 = indices[i * 3 + 1];
			unsigned __int32 i2 = indices[i * 3 + 2];

			// vertices of ith triangle.
			Vertex v0 = vertices[i0];
			Vertex v1 = vertices[i1];
			Vertex v2 = vertices[i2];

			// compute face normal.
			D3DXVECTOR3 e0 = v1.mPosition - v0.mPosition;
			D3DXVECTOR3 e1 = v2.mPosition - v0.mPosition;
			D3DXVECTOR3 faceNormal;
			D3DXVec3Cross(&faceNormal, &e0, &e1);

			// This triangle shares the following three vertices,
			// so add this face normal into the average of these
			// vertex normals.
			vertices[i0].mNormal += faceNormal;
			vertices[i1].mNormal += faceNormal;
			vertices[i2].mNormal += faceNormal;
		}

		// For each vertex v, we have added the face normals of all 
		// the triangles that share v, so now we just need to normalize.
		for (unsigned __int32 i = 0; i < mNumVertices; ++i)
			D3DXVec3Normalize(&vertices[i].mNormal, &vertices[i].mNormal);
	}

	void Land::initVertexBuffer(const Vertex * const vertices)
	{
		D3D10_BUFFER_DESC vertexBufferDescription;
		vertexBufferDescription.ByteWidth = sizeof(Vertex) * mNumVertices;
		vertexBufferDescription.Usage = D3D10_USAGE_IMMUTABLE; 
		vertexBufferDescription.BindFlags = D3D10_BIND_VERTEX_BUFFER; 
		vertexBufferDescription.CPUAccessFlags = 0; 
		vertexBufferDescription.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA vertexBufferData;
		vertexBufferData.pSysMem = vertices;

		HRESULT hr = mDevice->CreateBuffer(&vertexBufferDescription, &vertexBufferData, &mVertexBuffer);
		ErrorChecker(hr);		
	}

	void Land::initIndexBuffer(const unsigned __int32 * const indices)
	{
		D3D10_BUFFER_DESC indexBufferDescription;
		indexBufferDescription.ByteWidth = sizeof(unsigned __int32) * mNumFaces * 3;
		indexBufferDescription.Usage = D3D10_USAGE_IMMUTABLE;
		indexBufferDescription.BindFlags = D3D10_BIND_INDEX_BUFFER;
		indexBufferDescription.CPUAccessFlags = 0; 
		indexBufferDescription.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA indexBufferData;
		indexBufferData.pSysMem = indices;

		HRESULT hr = mDevice->CreateBuffer(&indexBufferDescription, &indexBufferData, &mIndexBuffer);
		ErrorChecker(hr);
	}
}